package cate.game.role.farm.adventure;

import cate.common.table.d.*;
import cate.common.table.farm.adventure.row.AdventureBossRow;
import cate.common.table.farm.adventure.row.AdventureCharpterRow;
import cate.common.table.role.vip.RoleVipRow;
import cate.common.util.GameResult;
import cate.game.activity.hangdrop.HangDropActivity;
import cate.game.client.msg.MergeMsgSender;
import cate.game.event.adventure.AdventurePassedIdChangeEvent;
import cate.game.play.part.FightSide;
import cate.game.play.proce.play.PlayPO;
import cate.game.res.MixRes;
import cate.game.res.MixResAdder;
import cate.game.res.MixResItem;
import cate.game.res.drop.DropConfigHelper;
import cate.game.role.Role;
import cate.game.role.farm.AbstractFarm;
import cate.game.role.farm.adventure.msg.AdventureDataResp;
import cate.game.role.farm.adventure.msg.AdventureHangResp;
import cate.game.role.farm.adventure.msg.WatchAdventureHangResp;
import cate.game.role.farm.adventure.task.AdventureTask;
import cate.game.role.fight.Position;
import com.fasterxml.jackson.annotation.JsonIgnore;
import easy.java.dev.note.NoteClass;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;
import org.apache.commons.lang3.StringUtils;

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@NoteClass(value = "主线")
public class Adventure extends AbstractFarm {
    @NoteField(value = "通过的关卡ID")
    public int combatIdPassed;

    @NoteField(value = "已领取的通关奖励的ID数组")
    public List<Integer> passRewardGot;

    @NoteField(value = "已领取的特殊奖励")
    public List<Integer> specialRewardGot;

    @NoteField(value = "上一次领取挂机收益的时间")
    public long lastHangGotTime;

    @NoteField(value = "上一次生成挂机收益的时间", detail = "实际用于结算的时间")
    public long lastBuildRewardTime;

    @NoteField(value = "之前观察时累计生成的基础奖励", detail = "前端可以只看不领取奖励")
    public MixRes baseRewardTemp;

    @NoteField(value = "之前观察时累计生成的活动掉落奖励")
    public Map<Integer, MixRes> hangDropActRewardsTemp;

    @NoteField(value = "今日de挑战次数记录")
    public QuickFightPlayTimes playTimes;

    @NoteField(value = "挂机随机掉落")
    @JsonIgnore
    public AdventureRandomDrop randomDrop;

    @NoteField(value = "探险任务")
    public AdventureTask task;

    @NoteField(value = "冒险boss")
    public AdventureBoss boss;

    @Override
    public void initialize(Role role) {
        super.initialize(role);
        if (passRewardGot == null) {
            passRewardGot = new ArrayList<>();
        }
        if (specialRewardGot == null) {
            specialRewardGot = new ArrayList<>();
        }
        if (lastHangGotTime == 0) {
            lastHangGotTime = System.currentTimeMillis();
            lastBuildRewardTime = lastHangGotTime;
        }
        if (baseRewardTemp == null) {
            baseRewardTemp = new MixRes();
        }
        if (playTimes == null) {
            playTimes = new QuickFightPlayTimes();
        }
        playTimes.initialize();
        if (randomDrop == null) {
            randomDrop = new AdventureRandomDrop();
        }
        randomDrop.initialize(role);
        if (hangDropActRewardsTemp == null) {
            hangDropActRewardsTemp = new HashMap<>();
        }
        if (task == null) {
            task = new AdventureTask();
        }
        task.initialize(role);
        if (boss == null) {
            boss = new AdventureBoss();
        }
        boss.initialize(role);
    }

    @Override
    public int getFightFuncId() {
        return GDFunc.ADVENTURE;
    }

    protected boolean checkPosition(int posNum) {
        for (int i = 1; i <= posNum; ++i) {
            Position position = role.getFight().getPosition(getFightFuncId(), posNum);
            if (position == null || position.grids.isEmpty()) {
                return false;
            }
        }
        return true;
    }

    public GameResult<Void> challenge() {
        GameResult<Void> r = new GameResult<>();
        if (fighting) {
            return r.fail("当前战斗未结束");
        }
        // 目的关卡ID
        int tarCombatId = this.combatIdPassed + 1;
        AdventureCharpterRow tpl = role.getGame().table.adventure.charpter.get(tarCombatId);
        if (tpl == null) {
            return r.fail( "你已经通过了所有关卡");
        }
        if (role.getBase().level < tpl.levelReq) {
            return r.fail("关卡等级要求:{}级", tpl.levelReq);
        }
        if (!checkPosition(tpl.getPosNum())) {
            return r.fail("当前关卡需要" + tpl.getPosNum() + "支队伍");
        }
        return doChallenge(tpl, GDPosition.DEFAULT_POS_NUM);
    }

    public GameResult<Void> doChallenge(AdventureCharpterRow tpl, byte posNum) {
        GameResult<Void> r = new GameResult<>();
//        AdventureFTC context = new AdventureFTC(role, tpl, posNum);
        AdventureCacheFTC context = new AdventureCacheFTC(role, tpl, posNum);
        context.playStory.level = tpl.id;
        context.playStory.playIndex = posNum;
        context.playStory.playMax = tpl.getPosNum();
        for (int i = 1; i < posNum; i++) {
            context.playStory.preWinner.add(GDFight.Team.A);
        }

        EcResult<FightSide> fsr = role.getGame().fight.side.createFightSideByCode(GDFight.Team.B, tpl.getInsCode(posNum));
        if (!fsr.ok()) {
            return r.fail(fsr.message);
        }
        EcResult<PlayPO> play_r = role.getGame().play.startByRole(role, context, fsr.data);
        if (!play_r.ok()) {
            return r.fail(play_r.message);
        }
        this.fighting = true;
        return r.success();
    }

    public void changeCombatIdPassed(int combatIdPassed) {
        this.combatIdPassed = combatIdPassed;
        publishEvent(role, new AdventurePassedIdChangeEvent(role, combatIdPassed));
    }

    @JsonIgnore
    public int getAdventurePassedNum() {
        return this.combatIdPassed;
    }

    public void noticeUpdate() {
        role.sendNow(new AdventureDataResp(role));
    }

    @Override
    public int getFuncId() {
        return GDFunc.ADVENTURE;
    }

    @Override
    public void onAfterOpen() {

    }

    @Override
    public void onDaySpan(boolean silence) {
        role.getRes().findBack.triggerByFuncId(GDFunc.QUICK_FIGHT);
        playTimes.reset();
        task.onDaySpan(silence);
        noticeUpdate();
    }

    /**
     * 领取或观察挂机奖励
     */
    @JsonIgnore
    public void getOrWatchHangReward(boolean watch) {
        getOrWatchHangReward(watch, 0);
    }

    /**
     * 领取或观察挂机奖励
     */
    @JsonIgnore
    public void getOrWatchHangReward(boolean watch, double adMultiple) {
        //最大挂机分钟数
        int max = role.getGame().table.vip.base.getByLevel(role.getBase().vip).hangMinites;
        final long MIN = 60000; // 一分钟
        long now = System.currentTimeMillis();
        // 距离上次领取奖励经过的时间
        int getPastMinutes = (int) ((now - lastHangGotTime) / MIN);
        //超过最大挂机时间
        if (getPastMinutes > max) {
            getPastMinutes = max;
        }
        //已经结算过的时间
        int settleMiutes = (int) ((lastBuildRewardTime - lastHangGotTime) / MIN);
        //本次需要结算的分钟数
        int minutes = getPastMinutes - settleMiutes;
        if (minutes <= 0) {
            minutes = 0;
        }
        // 根据挂机时间计算收益
        calAndSendReward(minutes, GDFunc.ADVENTURE_HANG, watch, adMultiple);
    }

    /**
     * 根据记录的时间换算出结算的分钟数
     */
    private int parseTime(long recordTime) {
        long MIN = 60000; // 一分钟
        long now = System.currentTimeMillis();
        long past = now - recordTime;
        int TIME_MAX = getHangMaxTime(); // 最多领取分钟

        int times = (int) (past / MIN);
        if (times > TIME_MAX) {
            times = TIME_MAX;
        }
        return times;
    }

    /**
     * @param times  本次随机的分钟数
     * @param funcId 功能id
     * @param watch  是否观察（如果只是观察则不发奖励）
     */
    public void calAndSendReward(int times, int funcId, boolean watch) {
        calAndSendReward(times, funcId, watch, 0);
    }

    @JsonIgnore
    private int getHangMaxTime() {
        return role.getGame().table.vip.base.getByLevel(role.getBase().vip).hangMinites;
    }

    public void calAndSendReward(int times, int funcId, boolean watch, double adMultiple) {
        // 本次生成的挂机奖励
        MixRes baseReward = new MixRes();

        if (times == 0 && !watch && baseRewardTemp.isEmpty()) {
            role.getGame().notice.message(role, 310104,"满一分钟才能领奖励");
            return;
        }
        // 时间超过1分钟才开始计算
        if (times > 0) {
            // 主线挂机本生产出的资源
            baseReward = buildBaseReward(times);
            if (baseReward.items.isEmpty()) {
                role.getGame().notice.message(role, "此通关数尚无对应奖励");
                return;
            }
        }
        // 活动掉落
        Map<Integer, MixRes> actDrops = new HashMap<>();
        role.getActivity().getEnableList(HangDropActivity.class).forEach(single -> {
            MixRes drop = single.calcHangDrop(times);
            if (drop != null && !drop.isEmpty()) {
                actDrops.put(single.funcId(), drop);
            }
        });

        //星格活动期间掉落翻倍
        if (role.getActivity().getEnableActivitySingle(GDFunc.Help.CROSS_SPICE_CALL_RANK) != null) {
            baseReward.itemMultiply(GDObj.Type.NUM, GDObj.Num.FORTUNE, 2);
        }

        MixRes show = new MixRes();
        // 主线挂机的奖励要和之前的一起发
        if (funcId == GDFunc.ADVENTURE_HANG) {
            baseReward.addList(randomDrop.getHangRandomDropReward(funcId, watch));
            this.baseRewardTemp.addList(baseReward.copy());
            int timeMax = getHangMaxTime();
            if (times < timeMax) {
                this.lastBuildRewardTime += 60000L * times;
            } else {
                this.lastBuildRewardTime = System.currentTimeMillis();
            }
            show.addList(baseRewardTemp.copy().multiplyDouble(1 + adMultiple));
            if (!actDrops.isEmpty()) {
                for (Map.Entry<Integer, MixRes> entry : actDrops.entrySet()) {
                    if (hangDropActRewardsTemp.containsKey(entry.getKey())) {
                        hangDropActRewardsTemp.get(entry.getKey()).addList(entry.getValue().copy());
                    } else {
                        hangDropActRewardsTemp.put(entry.getKey(), entry.getValue().copy());
                    }
                    show.addList(entry.getValue().copy());
                }
            }
            if (!watch) {
                role.getHistory().action.gotAdventureHangReward();
            }
        } else if (funcId == GDFunc.QUICK_FIGHT) {
            baseReward.addList(randomDrop.getQuickFightReward(times, funcId));
            for (Map.Entry<Integer, MixRes> entry : actDrops.entrySet()) {
                show.addList(entry.getValue().copy());
            }
            show.addList(baseReward.copy());
        }

        if (watch) {
            role.sendNow(new WatchAdventureHangResp(show));
        } else {
            MergeMsgSender sender = new MergeMsgSender();
            AdventureHangResp resp = new AdventureHangResp();
            resp.funcId = funcId;
            resp.reward = show;
            resp.recordOld(role);
            if (funcId == GDFunc.ADVENTURE_HANG) {
                resp.times = parseTime(lastHangGotTime);
                MixRes reward = baseRewardTemp.copy().multiplyDouble(1 + adMultiple);
                for (MixRes actRes : hangDropActRewardsTemp.values()) {
                    reward.addList(actRes.copy());
                }
                reward.add(role, true, GDOperation.ADVENTURE_HANG, sender);
                lastHangGotTime = System.currentTimeMillis();
                lastBuildRewardTime = lastHangGotTime;
                baseRewardTemp.clear();
                hangDropActRewardsTemp.clear();
            } else if (funcId == GDFunc.QUICK_FIGHT) {
                resp.times = times;
                for (MixRes actRes : actDrops.values()) {
                    baseReward.addList(actRes.copy());
                }
                new MixResAdder()
                        .setRes(baseReward)
                        .setOperation(GDOperation.ADVENTURE_QUICK_FIGHT)
                        .setMsgSending(sender)
                        .exe(role);
            }

            resp.recordAdd(role.getBase().level - resp.level_old, role.getBase().exp - resp.exp_old);
            resp.recordNew(role);
            role.sendNow(resp);
            sender.send(role, true);
            noticeUpdate();
            role.getHistory().action.manorGotHangReward();
        }
    }

    /**
     * 挂机掉落活动结束时移除缓存的奖励
     */
    public void removeOnHangDropActivityOver(int funcId) {
        hangDropActRewardsTemp.remove(funcId);
    }

    /**
     * 构建主线挂机本身功能的产出资源（不包括活动掉落）
     *
     * @param times 分钟
     * @return reward
     */
    public MixRes buildBaseReward(int times) {
        MixRes reward = new MixRes();
        if (times > 0) {
            AdventureCharpterRow combatTpl = getRow();
            if (combatTpl != null) {
                reward.parse(combatTpl.hangReward);
                // 固定奖励
                reward.multiply(times);
                reward.itemMultiply(GDObj.Type.NUM, GDObj.Num.EXP, calcVipScale(GDObj.Type.NUM, GDObj.Num.EXP));
                reward.itemMultiply(GDObj.Type.NUM, GDObj.Num.HERO_EXP, calcVipScale(GDObj.Type.NUM, GDObj.Num.HERO_EXP));
                reward.itemMultiply(GDObj.Type.NUM, GDObj.Num.M0, calcVipScale(GDObj.Type.NUM, GDObj.Num.M0));
                reward.itemMultiply(GDObj.Type.NUM, GDObj.Num.M1, calcVipScale(GDObj.Type.NUM, GDObj.Num.M1));
                reward.itemMultiply(GDObj.Type.NUM, GDObj.Num.INFO_VALUE, calcVipScale(GDObj.Type.NUM, GDObj.Num.INFO_VALUE));
                reward.itemMultiply(GDObj.Type.PROP, GDProp.TID.GRADE_STONE, calcVipScale(GDObj.Type.PROP, GDProp.TID.GRADE_STONE));
                // 随机奖励

                //                冒险boss加成
                AdventureBossRow bossRow = role.getGame().table.adventure.boss.get(boss.bossPassedId);
                if (bossRow != null) {
                    reward.addList(new MixRes(bossRow.hangAdd).multiply(times));
                }
            }
        }
        return reward;
    }

    public double calcVipScale(byte type, int tid) {
        RoleVipRow vipRow = role.getGame().table.vip.base.getByLevel(role.getBase().vip);
        NumberFormat format = NumberFormat.getNumberInstance();
        format.setMaximumFractionDigits(2);
        if (type == GDObj.Type.NUM && tid == GDObj.Num.M0) {
            return Double.parseDouble(format.format(vipRow.hangM0Ratio));
        } else if (type == GDObj.Type.NUM && tid == GDObj.Num.EXP) {
            return Double.parseDouble(format.format(vipRow.hangExpRatio));
        } else if (type == GDObj.Type.NUM && tid == GDObj.Num.HERO_EXP) {
            return Double.parseDouble(format.format(vipRow.hangHeroExpRatio));
        }
        return 1;
    }

    public long calcHangValue(byte type, int tid, long times) {
        AdventureCharpterRow advRow = getRow();
        if (advRow == null) {
            role.getGame().notice.message(role, "缺少冒险推图关卡配置");
            return 0;
        }
        double scale = calcVipScale(type, tid);
        double itemNum;
        if (type == GDObj.Type.PROP && tid == GDProp.TID.GRADE_STONE) {
            itemNum = advRow.everyHourStone;
        } else {
            MixRes hangReward = new MixRes(advRow.hangReward);
            itemNum = hangReward.count(type, tid);
        }
        if (itemNum > 0) {
            itemNum *= scale;
        }
        AdventureBossRow bossRow = role.getGame().table.adventure.boss.get(boss.bossPassedId);
        if (bossRow != null && StringUtils.isNotBlank(bossRow.hangAdd)) {
            MixRes hangAddReward = new MixRes(bossRow.hangAdd);
            for (MixResItem item : hangAddReward.items) {
                if (item.type == type && item.tid == tid) {
                    itemNum += item.num;
                }
            }
        }
        if (itemNum > 0) {
            itemNum *= times;
        }
        return (long) itemNum;
    }

    @JsonIgnore
    public AdventureCharpterRow getRow() {
        AdventureCharpterRow res;
        if (combatIdPassed == 0) {
            res = null;
        } else {
            res = role.getGame().table.adventure.charpter.get(combatIdPassed);
        }

        return res;
    }


    /**
     * 动态生成[主线关卡]奖励
     */
    public MixRes buildReward() {
        return buildReward(getRow().dropIdList);
    }

    /**
     * 动态生成奖励
     */
    private MixRes buildReward(List<Integer> list) {
        MixRes res = new MixRes();
        if (list == null) {
            return res;
        }
        for (Integer dropId : list) {
            res.addList(DropConfigHelper.getDropReward(role.getGame(), dropId));
        }
        return res;
    }

    public GameResult<Void> getPassReward(int tid) {
        GameResult<Void> r = new GameResult<>();
        AdventureCharpterRow passTpl = role.getGame().table.adventure.charpter.get(tid);
        if (passTpl == null || passTpl.extraReward == null || !passTpl.extraReward.contains(":")) {
            return r.fail( "不存在ID={}的通关奖励", tid);
        }
        if (this.passRewardGot.contains(tid)) {
            return r.fail("你已领取过通关奖励");
        }
        if (combatIdPassed < passTpl.id) {
            return r.fail( "必须通过{}关才能领取奖励", passTpl.section);
        }
        MixRes res = new MixRes(passTpl.extraReward);
        res.add(role, true, GDOperation.ADVENTURE_PASS_REWARD);
        role.getGame().notice.dialogReward(role, res);
        this.passRewardGot.add(tid);
        role.sendNow(new AdventureDataResp(role));
        return r.success();
    }

    public GameResult<Void> getSpecialReward(int tid) {
        GameResult<Void> r = new GameResult<>();
        AdventureCharpterRow passTpl = role.getGame().table.adventure.charpter.get(tid);
        if (passTpl == null || StringUtils.isBlank(passTpl.specialRewardStr)) {
            return r.fail( "不存在的奖励");
        }
        if (this.specialRewardGot.contains(tid)) {
            return r.fail( "你已领取过特殊奖励");
        }
        if (combatIdPassed < passTpl.id) {
            return r.fail( "必须通过{}关才能领取奖励", passTpl.section);
        }
        this.specialRewardGot.add(tid);
        new MixResAdder()
                .setRes(passTpl.specialRewardStr)
                .setDialogReward(true)
                .setOperation(GDOperation.ADVENTURE_SPECIAL_REWARD)
                .setSendMailWhenFull(true)
                .exe(role);
        noticeUpdate();
        role.getRes().achievement.checkOpen();
        return r.success();
    }

    public boolean allSpecialRewardGot() {
        for (Integer rewardId : role.getGame().table.adventure.charpter.specialRewardIdList) {
            if (!specialRewardGot.contains(rewardId)) {
                return false;
            }
        }
        return true;
    }

    public void resetRewardTime() {
        lastBuildRewardTime = System.currentTimeMillis();
        lastHangGotTime = System.currentTimeMillis();
        randomDrop.resetRewardTime();
        noticeUpdate();
    }
}
